package com.cy.pj.service.impl;

import com.cy.pj.Dao.SysUserDao;
import com.cy.pj.Dao.SysUserRoleDao;
import com.cy.pj.common.exception业务异常.ServiceException;
import com.cy.pj.common.pojo.SysUser;
import com.cy.pj.service.SysUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;

@Transactional(readOnly = false,
rollbackFor = Throwable.class,
isolation = Isolation.READ_COMMITTED)
@Service
public class SysUserServiceImpl implements SysUserService {
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysUserRoleDao sysUserRoleDao;

    //客户端修改密码的业务
    @Transactional
    @Override
    public int updatePassword(String sourcePassword, String newPassword, String confirmPassword) {
        //1.参数校验
if(sourcePassword==null||" ".equals(sourcePassword))
throw new IllegalArgumentException("原密码不能为空");
if(newPassword==null||"".equals(newPassword))
throw new IllegalArgumentException("新密码不能为空");
if(!newPassword.equals(confirmPassword))
throw new IllegalArgumentException("两次输入的新密码不一致");
//2.获取登录用户，并判定登录用户的密码和用户输入的原密码是否一致
      SysUser user= (SysUser) SecurityUtils.getSubject().getPrincipal();
        //对输入的旧密码进行MD5算法加密比对
SimpleHash sh=new SimpleHash("MD5",sourcePassword,user.getSalt(),1);
if(!user.getPassword().equals(sh.toHex()))//MD5加密，相同内容加密结果也相同
throw new IllegalArgumentException("原密码不正确");
        //3.对新密码进行加密
        String salt=UUID.randomUUID().toString();
        //simpleHash--借助shiro框架里的api工具类，对密码加密
        SimpleHash newpassword=new SimpleHash("MD5",confirmPassword,salt,1);
        String newHashedPassword=newpassword.toHex();//默认是2进制，转化为16进制
        //3.更新用户密码
        int rows=sysUserDao.updatePassword(newHashedPassword,salt,user.getId());
        return rows;
    }

    //@RequiresPermissions("sys:user:view")-只有这个方法的授权注解不能放这里，放到Controller层上
    @Transactional(readOnly = true)
    @Override
    public List<SysUser> findUsers(SysUser sysUser) {
        return sysUserDao.selectUsers(sysUser);
    }

    @Transactional(readOnly = true)
    @Override
    public SysUser findById(Integer id) {
        //基于id查询用户以及用户对应部门信息
        SysUser sysUser=sysUserDao.selectById(id);
        if(sysUser==null)
        throw new ServiceException("用户不存在");
       //基于id查询用户对应的角色id
        List<Integer> roleIds = sysUserRoleDao.selectRoleIdsByUserId(id);
        //最后把角色id用set注入到结果集里
        sysUser.setRoleIds(roleIds);
        return sysUser;
    }

    @Override
    public int saveUser(SysUser sysUser) {
        //1.保存用户自身信息
        //对密码进行MD5加密(MD5是一种不可逆的加密算法，只能加密不能解密，相同内容加密结果也相同)
        String password=sysUser.getPassword();
        //salt:盐值--产生个随机字符串，和密码放在一起加密
        String salt= UUID.randomUUID().toString();
        //simpleHash--借助shiro框架里的api工具类，对密码加密
        // --(算法名称,加密的密码,加密时使用的盐值,要加密几次(默认加密一次))
        SimpleHash simpleHash=
                new SimpleHash("MD5", password, salt, 1);
        //已加密过的密码,默认是2进制的,要转换成16进制的
        password=simpleHash.toHex();
           //加密过的密码，盐值注入到pojo里
        sysUser.setPassword(password);
        sysUser.setSalt(salt);

        int rows=sysUserDao.insertUser(sysUser);
        //2.保存用户角色关系数据
        sysUserRoleDao.insertUserRoles(sysUser.getId(),sysUser.getRoleIds());
        return rows;
    }

    @Transactional
    @Override
    public int updateUser(SysUser sysUser) {
     //更新用户自身信息
        int row= sysUserDao.updateUser(sysUser);
        if(row==0)
            throw new ServiceException("用户可能已经不存在");
   //更新用户和角色关系数据
        sysUserRoleDao.deleteByUserId(sysUser.getId());
sysUserRoleDao.insertUserRoles(sysUser.getId(),sysUser.getRoleIds());
        return row;
    }

    /**
     *@Transactional 描述方法时，此方法为一个事务切入点方法
     *
     *@RequiresPermissions 描述方法时，此方法为一个授权切入点方法--我们在
     * 访问此方法时就需要授权，有权限则可以访问，没有权限则抛出异常。
     * 那如何判定用户有没有访问此方法的权限呢?
     *
     * 这个方法的权限检测调用流程分析:
     * subject->SecurityManager->Authorize->Realm
     *
     * 当我们在方法时，shiro框架底层会获取此方法上的@RequiresPermissions注解，进而取到注解中的权限标识，
     *然后会调用subject对象的checkpermissions(权限标识)方法把注解中的权限标识提交给SecurityManager，而SecurityManager
     * 调用授权管理器(Authorizer)比对用户是否有权限。(realm基于用户id，从数据库获取了用户的授权标识，再返回给Authorizer作比对)
     */
    @Transactional
    @Override
    public int validById(Integer id, Integer valid) {
        //获取登录用户--登陆成功后，用户信息会放在Session里面，从Session里获取
        /**SecurityUtils是一个抽象的工具类，提供了SecurityManager实例的保存和获取方法，以及创建Subject的方法。
        SecurityUtils提供了getSecurityManager（）和setSecurityManager方法，
         还有个特殊的方法getSubject（），这是获取主体的最有效的途径
         */
        SysUser user= (SysUser) SecurityUtils.getSubject().getPrincipal();
        String modifiedUser=user.getUsername();

        return sysUserDao.validById(id,valid,modifiedUser);
    }
}
